﻿// Copyright (c) 2014 Morten Bakkedal
// This code is published under the MIT License.

using System;

using FuncLib.DualFunctions;
using FuncLib.Functions;

namespace FuncLibSamples.Samples
{
	public class DualFunctionSamples
	{
		public static void Sample1()
		{
			DualVariable x = new DualVariable();
			DualVariable y = new DualVariable();

			DualFunction f = x * DualFunction.Exp(x * y);
			Function g = x * Function.Exp(x * y);

			// g is implicitly a DualFunction, despite Function operators have been used.
			//Console.WriteLine("g is a DualFunction: {0}", g is DualFunction);

			// Thus the evaluations in the following two expressions are exactly the same.
			Console.WriteLine(f.Value(x | 0.2, y | 0.6));
			//Console.WriteLine(g.Value(x | 0.2, y | 0.6));
		}

		public static void Sample2()
		{
			DualVariable x = new DualVariable("x", 2);
			DualVariable y = new DualVariable("y", 2);

			DualFunction f, g;

			//f.Compile().Gradient
			/*f = DualFunction.Cos(x) * DualFunction.Exp(y);
			f = DualFunction.Cos(f);
			f = DualFunction.Exp(f);
			f = DualFunction.Cos(f);
			g = DualFunction.Cos(x * DualFunction.Exp(y));
			f = f / g;*/

			f = DualFunction.Sin(x * DualFunction.Log(y)) + 2.0;
			g = DualFunction.Cos(DualFunction.Exp(x) * y);
			f = f * g;
			f = DualFunction.Pow(f, g);
			f = DualFunction.Sqr(f);
			f = DualFunction.Cos(f);
			f = DualFunction.Cos(f);
			f = DualFunction.Exp(f);
			f = DualFunction.Sin(f);
			f = DualFunction.Cos(f);
			f = DualFunction.Cos(f);
			f = DualFunction.Exp(f);
			f = DualFunction.Sin(f);
			f *= DualFunction.Exp(x) * DualFunction.Exp(x * y);
			f = DualFunction.Exp(f);
			f = DualFunction.Sqrt(f);
			f = x + DualFunction.Exp(f);
			f = DualFunction.Exp(f);
			f = DualFunction.Sqr(f);
			f = f / (x * DualFunction.Exp(y) / y);
			f = DualFunction.Log(f);
			f = DualFunction.Cos(f);
			f = DualFunction.Exp(f);
			f *= (DualFunction.Exp(x + y) * DualFunction.Exp(y) - 0.2) / 1.234;
			f = 4.321 / f;
			f = -f;
			f = f * f;
			f = 0.4 - f;
			f = DualFunction.Sin(f);
			f = DualFunction.Sin(f);
			f = DualFunction.Exp(f);
			f = DualFunction.Pow(f, g);

			Point p = new Point(x | 0.2, y | 0.1);

			//f.Compile().GeneratedCode;

			//Console.WriteLine(new DualCodeGenerator(f).GeneratedCode);
			//Console.WriteLine(f.Compile().GeneratedCode);

			try
			{
				Console.WriteLine(f.Value(p));
				Console.WriteLine(f.TestFunction.Value(p));
			}
			catch (DualVariableOrderExceededException)
			{
				Console.WriteLine("NA");
			}

			try
			{
				Console.WriteLine(f.Derivative(x).Value(p));
				Console.WriteLine(f.TestFunction.Derivative(x).Value(p));
			}
			catch (DualVariableOrderExceededException)
			{
				Console.WriteLine("NA");
			}

			try
			{
				Console.WriteLine(f.Derivative(x, 2).Value(p));
				Console.WriteLine(f.TestFunction.Derivative(x, 2).Value(p));
			}
			catch (DualVariableOrderExceededException)
			{
				Console.WriteLine("NA");
			}

			try
			{
				Console.WriteLine(f.Derivative(y).Value(p));
				Console.WriteLine(f.TestFunction.Derivative(y).Value(p));
			}
			catch (DualVariableOrderExceededException)
			{
				Console.WriteLine("NA");
			}

			try
			{
				Console.WriteLine(f.Derivative(x, y).Value(p));
				Console.WriteLine(f.TestFunction.Derivative(x, y).Value(p));
			}
			catch (DualVariableOrderExceededException)
			{
				Console.WriteLine("NA");
			}

			try
			{
				Console.WriteLine(f.Derivative(y, 2).Value(p));
				Console.WriteLine(f.TestFunction.Derivative(y, 2).Value(p));
			}
			catch (DualVariableOrderExceededException)
			{
				Console.WriteLine("NA");
			}
		}
	}
}
